// -*-c++-*- Copyright (C) 2011 osgPango Development Team
// $Id$

#ifndef OSGPANGO_GLYPHCACHE
#define OSGPANGO_GLYPHCACHE

#include <map>
#include <osg/Geometry>
#include <osg/Texture>
#include <osgCairo/Image>
#include <osgPango/Export>

#include <pango/pangocairo.h>

namespace osgPango {

class Font;
class GlyphRenderer;

//! A small convenience structure used internally by the caching object to keep track
//! of the various bits of positioning, sizing, and image index information for each
//! glyph it manages.
struct OSGPANGO_EXPORT CachedGlyph {
	CachedGlyph(
		unsigned int     img    = 0,
		const osg::Vec2& origin = osg::Vec2(),
		const osg::Vec2& size   = osg::Vec2(),
		const osg::Vec2& bl     = osg::Vec2(),
		const osg::Vec2& br     = osg::Vec2(),
		const osg::Vec2& ur     = osg::Vec2(),
		const osg::Vec2& ul     = osg::Vec2()
	);

	unsigned int img;
	osg::Vec2    origin;
	osg::Vec2    size;
	osg::Vec2    bl;
	osg::Vec2    br;
	osg::Vec2    ur;
	osg::Vec2    ul;
};

//! There is one GlyphCache object PER FONT object (determined by the font hash). GlyphRenderer
//! objects contain the GlyphCaches.
class OSGPANGO_EXPORT GlyphCache: public osg::Object {
public:
	//! This type maps the glyph's numeric index to the CachedGlyph structure.
	typedef std::map<unsigned int, CachedGlyph> GlyphMap;

	//! This type represents the osgCairo::Image and Texture2D stored internally for a single
	//! "page" of the glyph texture atlas.
	typedef std::pair<osg::ref_ptr<osgCairo::Image>, osg::ref_ptr<osg::Texture> > CairoTexture;

	//! This type contains a single Image/Texture combo for EACH page of the texture atlas.
	typedef std::vector<CairoTexture> Images;

	//! Since a GlyphRenderer may contain many layers per glyph, this last type represents each
	//! layer, numerically ordered.
	typedef std::vector<Images> Layers;

	META_Object(osgPango, GlyphCache);

	//! This constructor is generally only called by the GlyphRenderer; it passes itself in as
	//! the "parent"; a GlyphCache should not be created without a parent GlypRenderer.
	GlyphCache(GlyphRenderer* renderer = 0);

	GlyphCache(const GlyphCache& gc, const osg::CopyOp& copyOp);

	//! Returns the CachedGlyph structure data associated with the given glyph index.
	const CachedGlyph* getCachedGlyph(unsigned int glyph);

	//! Creates the CachedGlyph (if it doesn't exists) based on the passed in glyph information
	//! and returns it; if the glyph already exists, it is simply returned.
	const CachedGlyph* createCachedGlyph(PangoFont* font, PangoGlyphInfo* glyphInfo);

	//! Returns the amount of GPU memory usage in bytes, taking into account all layers.
	unsigned long getMemoryUsageInBytes () const;

	osg::Texture* getTexture(unsigned int index, unsigned int layerIndex) {
		return _getTexture(index, layerIndex);
	}
	
	const osg::Texture* getTexture(unsigned int index, unsigned int layerIndex) const {
		return _getTexture(index, layerIndex);
	}

	const GlyphRenderer* getGlyphRenderer() const {
		return _renderer.get();
	}

	//! Attempts to set the GlyphRenderer; returns true if the operation was successful.
	//! Ideally, you will never even CREATE a GlyphCache without having access to a
	//! parent GlyphRenderer, but in some cases (such as serialization), this is
	//! unavoidable. However, this operation will fail if GlyphRenderer is already valid.
	bool setGlyphRenderer(GlyphRenderer* renderer);

	Layers& getLayers() { 
		return _layers; 
	}
	
	const Layers& getLayers() const {
		return _layers;
	}
	
	GlyphMap& getGlyphMap() {
		return _glyphs;
	}
	
	const GlyphMap& getGlyphMap() const {
		return _glyphs;
	}

	//! Fetches the current XYH position (or perhaps, "cursor", if you prefer) of the
	//! GlyphCache's internal rendering backend.
	const osg::Vec3f& getXYH() const {
		return _xyh;
	} 

	//! This should only ever be used by the OSG serialization routines; don't go messing
	//! about!
	void setXYH(const osg::Vec3f& xyh) {
		_xyh = xyh;
	}

	//! Returns the hash key used to map this particular GlyphCache object inside the
	//! parent GlyphRenderer.
	guint getHash() const {
		return _hash;
	}

	//! Sets the hash key; this is only used by the serialization routines.
	void setHash(guint hash) {
		_hash = hash;
	}

	//! Returns the Pango font description string used to generate this
	//! GlyphCache instances hash.
	const std::string& getDescription() const {
		return _description;
	}

	//! Sets the description string; this is only used by the backend and during
	//! serialization.
	void setDescription(const std::string& description) {
		_description = description;
	}

private:
	bool _newImageAndTexture();

	osgCairo::Image* _getImage   (unsigned int, unsigned int) const;
	osg::Texture*    _getTexture (unsigned int, unsigned int) const;

	//! The "renderer" object, not bound to any local data.
	osg::observer_ptr<GlyphRenderer> _renderer;

	Layers      _layers;
	GlyphMap    _glyphs;
	osg::Vec3f  _xyh;
	guint       _hash;
	std::string _description;
};

//! This is a small subclass of osg::Geometry that simplifies a lot of the tedious Geometry
//! Manipulation we are required to do.
class OSGPANGO_EXPORT GlyphGeometry: public osg::Geometry {
public:
	GlyphGeometry();
	GlyphGeometry(const GlyphGeometry& gg, const osg::CopyOp& copyOp);

	META_Object(osgPango, GlyphGeometry);

	bool finalize          ();
	bool pushCachedGlyphAt (const CachedGlyph* glyph, const osg::Vec2& position);

private:
	unsigned int _numQuads;
};

// We don't use an osg::ref_ptr here becase the Geometry will actually only be temporary.
// The newly allocated osg::Geometry will passed to an osg::Geode during finalize, and
// the Geode will properly dictate when the resources are freed.
typedef std::map<unsigned int, GlyphGeometry*> GlyphGeometryIndex;

}

#endif
